/**
 * @file Wizard
 * @author liaoxuezhi@baidu.com
 */
/* eslint-disable */

import * as React from 'react';
import {
  Component,
  PropTypes
} from 'react';
import {Renderer, render, buildApi, noop, filter, normalizeLink, evalExpression} from '../renderer';
import * as cx from 'classnames';
import * as actions from './actions';
import * as assign from 'object-assign';
import reducer from './reducer';
import './wizard.scss';
import {ValidationError} from '../../util/errors';
import {push, goBack} from 'react-router-redux';
import {alert, confirm} from '../../widgets/alert/alert';
import OnExpression from '../hoc/on-expression';
import * as schema from './wizard-schema.json';
import {merge} from '../../util/helper';

@Renderer({
  name: 'wizard',
  desc: '...',
  test: /(^|\/)wizard$/,
  reducer: reducer,
  schema
}, null, assign({
  push,
  goBack
}, actions))
@OnExpression
export default class Wizard extends Component {
  static propTypes = {
    className: PropTypes.string,
    bulkSubmit: PropTypes.bool,
    initApi: PropTypes.string,
    messages: PropTypes.shape({
      fetchSuccess: PropTypes.string,
      fetchFailed: PropTypes.string,
      saveFailed: PropTypes.string,
      saveSuccess: PropTypes.string
    }),
    data: PropTypes.object,
    locals: PropTypes.object,
    name: PropTypes.string,
    sendNotify: PropTypes.func.isRequired
  };

  static defaultProps = {
    formRef: noop,
    bulkSubmit: false,
    readOnly: false,
    messages: {},
    data: {},
    locals: {},
    actionClassName: 'btn-sm btn-default',
    actionPrevLabel: '上一步',
    actionNextLabel: '下一步',
    actionNextSaveLabel: '保存并下一步',
    actionFinishLabel: '完成',
    persistStore: false
  };

  static contextTypes = {
    scoped: PropTypes.object
  };

  constructor(props) {
    super(props);

    this.model = null;
    this.state = {
      action: null,
      currentStep: parseInt(props.step, 10) || 1,
      data: assign({}, props.data || {}),
      isPristine: true
    };

    this.handleFormInit = this.handleFormInit.bind(this);
    this.handleFormChange = this.handleFormChange.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
    this.handleAction = this.handleAction.bind(this);
    this.gotoStep = this.gotoStep.bind(this);
    this.prevStep = this.prevStep.bind(this);
    this.nextStep = this.nextStep.bind(this);
    this.redirect = this.redirect.bind(this);
  }

  componentWillMount() {
    const scoped = this.context.scoped;
    scoped && scoped.registerComponent && scoped.registerComponent(this);
  }

  componentDidMount() {
    const {
      initApi,
      fetchWizard,
      scopedKey,
      locals
    } = this.props;

    if (initApi) {
      fetchWizard(scopedKey, initApi, this.state.data, locals);
    }
  }

  componentWillReceiveProps(nextProps) {
    const props = this.props;
    const steps = nextProps.steps;
    const currentStep = this.state.currentStep;
    const state = nextProps.state;
    const data = nextProps.data;
    const sendNotify = nextProps.sendNotify;

    if (props.state.data !== nextProps.state.data) {
      this.setState({
        data: assign({}, this.state.data, this.model, data, state.data)
      });
    }

    if (props.data !== nextProps.data) {
      this.setState({
        data: assign({}, this.model, state.data, data)
      });
    }

    // 提示获取数据情况
    if (props.state.fetching && !nextProps.state.fetching) {
      const {
        state: {
          invalid,
          error,
          message
        },
        messages,
      } = nextProps;

      if (invalid) {
        let msg = messages.fetchFailed || (error instanceof Error ? error.message : message) || '获取数据失败';
        sendNotify({
          message: msg,
          level: 'error'
        });
      } else if (messages.fetchSuccess) {
        sendNotify({
          message: messages.fetchSuccess,
          level: 'success'
        });
      }
    }

    if (props.state.action.saving && !nextProps.state.action.saving) {
      const {
        state: {
          action: {
            invalid,
            error,
            message
          }
        },
        sendNotify
      } = nextProps;
      const action = this.state.action;

      let msg= action.messages && action.messages.success || '操作成功';

      if (invalid) {
        msg = action.messages && action.messages.failed || (error instanceof Error ? error.message : message) || '操作失败';
      }

      sendNotify({
        message: msg,
        level: invalid ? 'error' : 'success'
      });
    }

    // 提示保存数据情况.
    if (props.state.saving && !nextProps.state.saving) {
      const {
        state: {
          invalid,
          error,
          message,
          data
        },
        messages,
        onSaved,
      } = nextProps;
      const action = this.state.action;

      let msg = action && action.messages && action.messages.success || messages.saveSuccess || message || '保存成功';

      if (invalid) {
        msg = action && action.messages && action.messages.failed || messages.saveFailed || (error instanceof Error ? error.message : message) || '保存失败';
      }

      sendNotify({
        message: msg,
        level: invalid ? 'error' : 'success'
      });

      if (!invalid) {
        if (currentStep === steps.length) {
          onSaved && onSaved();
          const redirect = action && action.redirect || nextProps.redirect;
          redirect && this.redirect(redirect, data);
        } else {
          this.nextStep();
        }
      }
    }
  }

  componentWillUnMount() {
    const scoped = this.context.scoped;
    scoped && scoped.unRegisterComponent && scoped.unRegisterComponent(this);

    if (!this.props.persistStore) {
      this.props.resetWizard(this.props.scopedKey);
    }
  }

  handleFormInit(model) {
    const {
      scopedKey,
      fetchStep,
      data,
      steps,
      locals,
      state,
      onInit
    } = this.props;
    const currentStep = this.state.currentStep;
    const step = steps[currentStep - 1];

    this.model = model;
    const newData = assign({}, this.state.data, data, state.data, model, {
      step: currentStep,
      steps: steps.length
    });

    this.setState({
      data: newData,
      isPristine: true
    }, () => {
      if (step.initApi) {
        fetchStep(scopedKey, step.initApi, this.state.data, locals);
      }

      if (onInit) {
        onInit(newData);
      }
    });
  }

  handleFormChange(model) {
    const {
      onChange
    } = this.props;

    this.model = model;
    model = assign({}, this.state.data, model);
    onChange && onChange(model);

    this.setState({
      data: model,
      isPristine: false
    });
  }

  handleFormSubmit(values) {
    const {
      saveStep,
      updateStep,
      scopedKey,
      locals,
      steps,
      bulkSubmit,
      doAction,
      target
    } = this.props;
    const currentStep = this.state.currentStep;
    const step = steps[currentStep - 1];
    const data = merge(this.state.data, values, {
      step: currentStep,
      steps: steps.length
    });
    const action = this.state.action;

    if (action && (action.api)) {
      doAction(scopedKey, action.api, data, locals);
    } else if (step.api || !bulkSubmit) {
      if (!step.api) {
        alert('Step 上没有配置 api, 不能提交');
        return;
      }

      saveStep(scopedKey, step.api, data, locals);
    } else {
      updateStep(scopedKey, data);
      // this.nextStep();
      setTimeout(this.nextStep, 200);
    }
  }

  handleAction(action) {
    const {
      scopedKey,
      doAction,
      locals
    } = this.props;

    const model = this.form.getModel();
    const data = assign({}, this.state.data, model);

    if (action.url || action.actionType === 'url' || action.link || action.actionType == 'link') {
      if (action.type === 'submit') {
        this.setState({
          action
        }, () => this.form.submit());
      } else {
        const push = this.props.push;
        const goBack = this.props.goBack;
        let fn = () => action.link === 'goBack' || action.action === 'goBack' ? goBack() : push(buildApi(action.link || action.action, data).url);

        if (action.actionType === 'url' || action.url) {
          fn = () => window.open(buildApi(action.link || action.action, data).url);
        }

        return action.confirmText ? confirm(filter(action.confirmText, data)).then(fn) : fn();
      }

    } else if (action.type === 'reset' || action.actionType === 'reset') {
      this.form.reset();
    } else if (action.type === 'button' && action.actionType === 'ajax') {
      this.setState({
        action
      }, () => doAction(scopedKey, action.api || action.action, data, locals));
    } else if (action.actionType === 'prev') {
      return this.prevStep();
    } else if (action.actionType === 'next') {
      this.setState({
        action
      }, () => this.form.submit());
    } else {
      this.props.onRequestAction && this.props.onRequestAction(action);
    }
    // else {
    //   this.props.onRequestAction && this.props.onRequestAction(action);
    // }
  }

  gotoStep(step, cb) {
    this.setState({
      currentStep: step,
      data: assign({}, this.state.data, {
        step
      })
    }, cb);
  }

  prevStep() {
    const currentStep = this.state.currentStep;

    if (currentStep > 1) {
      this.gotoStep(currentStep - 1);
    }
  }

  nextStep() {
    const steps = this.props.steps;
    const currentStep = this.state.currentStep;

    if (currentStep < steps.length) {
      this.gotoStep(currentStep + 1);
    } else {
      const {
        saveWizard,
        bulkSubmit,
        locals,
        scopedKey,
        api,
        onSubmit
      } = this.props;

      if (bulkSubmit) {

        if (!api && !onSubmit) {
          return alert('Wizard 中没有配置 api, 不能保存');
        }

        const model = this.state.data;

        if (onSubmit) {
          onSubmit(model);
        }

        if (api) {
          saveWizard(scopedKey, api, model, locals);
        }
      }
    }
  }

  redirect(url, data) {
    url = filter(url, data || this.state.data);
    const {
      push,
      goBack
    } = this.props;

    if (url === 'goBack') {
      goBack();
    } else {
      url = normalizeLink(url);
      push(url);
    }
  }

  renderSteps() {
    const {
      steps
    } = this.props;
    const currentStep = this.state.currentStep;

    return (
      <div className="wizard-steps clearfix" id="form-wizard">
        {steps && steps.length ? (
          <ul className="steps">
            {steps.map((step, key) => {
              const canJump = key + 1 < currentStep || step.jumpable
                || step.jumpableOn && evalExpression(step.jumpableOn, this.state.data);

              return (
                <li
                  key={key}
                  className={cx({
                  'complete': canJump,
                  'active': currentStep === key + 1
                })}
                  onClick={() => canJump ? this.gotoStep(key + 1) : null}
                >
                <span
                  className={cx("badge", {
                    'badge-success': canJump && currentStep != key + 1,
                    'badge-info': currentStep === key + 1
                  })}
                >{key + 1}</span>
                  {step.title || step.label || `第 ${key + 1} 步`}
                </li>
              );
            })}
          </ul>
        ) : null}
      </div>
    );
  }

  renderActions() {
    const {
      steps,
      state,
      readOnly,
      bulkSubmit,
      disabled,
      actionClassName,
      actionPrevLabel,
      actionNextLabel,
      actionNextSaveLabel,
      actionFinishLabel,
      $path
    } = this.props;
    const currentStepIndex = this.state.currentStep;
    // const currentStep = steps[currentStepIndex - 1];
    const nextStep = steps[currentStepIndex];
    const prevStep = steps[currentStepIndex - 2];
    const waiting = state.fetching || state.saving || state.action.saving;

    return (
      <div className="panel-footer">
        {render(`${$path}/action`, {
          type: 'button',
          label: actionPrevLabel,
          actionType: 'prev',
          className: actionClassName
        }, {
          disabled: waiting || !prevStep || disabled,
          values: this.state.data,
          detectPropName: 'values',
          onRequestAction: this.handleAction
        })}

        {render(`${$path}/action`, {
          type: 'button',
          label: !nextStep ? actionFinishLabel : bulkSubmit ? actionNextLabel : actionNextSaveLabel,
          actionType: 'next',
          primary: !nextStep || !bulkSubmit,
          className: actionClassName
        }, {
          disabled: waiting || disabled || readOnly && (!bulkSubmit || !nextStep),
          values: this.state.data,
          detectPropName: 'values',
          onRequestAction: this.handleAction
        })}
      </div>
    );
  }

  render() {
    const {
      className,
      steps,
      locals,
      disabled,
      readOnly,
      formRef,
      state,
      $path
    } = this.props;
    const currentStep = this.state.currentStep;
    const step = steps[currentStep - 1];

    return (
      <div className={cx("panel panel-default wizard", className)}>
        {this.renderSteps()}
        {step ? (
          <div className="step-content clearfix">
            {render(`${$path}/basic-form`, step, {
              key: currentStep,
              locals,
              formRef: form => formRef(this.form = form),
              errors: state.error instanceof ValidationError ? state.error.detail : null,
              data: this.state.data,
              loading: state.fetching || state.saving || state.action.saving,
              disabled: disabled,
              readOnly: readOnly,
              onInit: this.handleFormInit,
              onChange: this.handleFormChange,
              onSubmit: this.handleFormSubmit,
              onRequestAction: this.handleAction
            })}
          </div>
        ) : (
          <div className="step-content clearfix">
            <p className="alert alert-danger">配置错误</p>
          </div>
        )}
        {this.renderActions()}
      </div>
    );
  }
}
